package com.wzy.media.service.jobhandler;

import com.wzy.base.exception.CustomException;
import com.wzy.base.utils.Mp4VideoUtil;
import com.wzy.media.model.po.MediaProcess;
import com.wzy.media.service.MediaFileProcessService;
import com.wzy.media.service.MediaFileService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.*;

/**
 * @Author wzy
 * @Date 2024/1/15 20:59
 * @description: 视频任务处理类
 */
@Component
@Slf4j
public class VideoTask {
    @Autowired
    private MediaFileProcessService mediaFileProcessService;
    @Autowired
    private MediaFileService mediaFileService;

    //ffmpeg路径
    @Value("${videoprocess.ffmpegpath}")
    private String ffmpeg_path;

    /**
     * 2、分片广播任务
     */
    @XxlJob("videoJobHandler")
    public void videoJobHandler() throws Exception {

        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();//执行器序号 从0开始
        int shardTotal = XxlJobHelper.getShardTotal();//执行器总数
        //查询待处理的任务
        //取出CPU核心数作为一次处理数据的条数
        int processors = Runtime.getRuntime().availableProcessors();
        //一次处理视频的数量不要超过cpu核心数
        List<MediaProcess> mediaProcessList = mediaFileProcessService.selectListByShardIndex(shardTotal,shardIndex, processors);
        int size = mediaProcessList.size();
        log.debug("取出待处理任务{}条",mediaProcessList.size());
        if(size <= 0){
            return;
        }
        // 创建一个线程数为size的线程池
        ExecutorService executor = Executors.newFixedThreadPool(size);
        CountDownLatch countDownLatch = new CountDownLatch(size);

        for (MediaProcess mediaProcess : mediaProcessList) {
            //将任务加入线程池
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        //开启任务
                        Boolean b = mediaFileProcessService.startTask(mediaProcess);
                        if(!b){ //获取分布式锁失败 开启任务失败
                            log.debug("抢占任务失败，任务id{}",mediaProcess.getId());
                            return;
                        }
                        //执行视频转码
                        log.debug("开始执行任务:{}", mediaProcess);
                        //桶
                        String bucket = mediaProcess.getBucket();
                        //视频文件的id
                        String fileId = mediaProcess.getFileId();
                        //视屏文件在minio中的存储路径
                        String filePath = mediaProcess.getFilePath();
                        //下载minio中的视频到本地
                        File file = mediaFileService.downloadFileFromMinIO(bucket, filePath);
                        if(file==null){
                            log.debug("下载待处理文件失败,originalFile:{}", bucket.concat(filePath));
                            //下载视频文件失败 修改任务处理状态 后返回
                            mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(),"3",fileId,null,"视频文件下载失败");
                            return;
                        }
                        //否则下载成功 然后处理下载文件
                        //源avi视频的路径
                        String video_path = file.getAbsolutePath();
                        //转换后mp4文件的名称
                        String mp4_name = fileId+".mp4";
                        //转换后mp4文件的路径
                        //先创建一个临时文件 作为转换后的MP4文件、
                        File tempFile = null;
                        try {
                             tempFile = File.createTempFile("minio", ".mp4");
                        } catch (IOException e) {
                            log.error("创建mp4临时文件失败");
                            mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, "创建mp4临时文件失败");
                            return;
                        }
                        //转码后的MP4路径
                        String mp4_path = tempFile.getAbsolutePath();
                        //创建工具类对象
                        Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpeg_path,video_path,mp4_name,mp4_path);
                        //开始视频转换，成功将返回success
                        String result = videoUtil.generateMp4();
                        if(!result.equals("success")){
                            //视频转码失败
                            log.debug("视频转码失败，原因{}，bucket{}，objectName{}",result,bucket, filePath);
                            mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(),"3",fileId,null,result);
                            return;
                        }
                        //如果转码成功 则将转码后的视频上传到minio
                        //mp4在minio中的存储路径 将原视屏路径file_path 后缀改为.mp4
                        String objectName = filePath.substring(0, filePath.lastIndexOf(".")).concat(".mp4");
                        //上传到minio
                        boolean b1 = mediaFileService.addMediaFilesToMinIO(mp4_path, "video/mp4", bucket, objectName);
                        if(!b1){
                            //转码后视频上传到minio失败
                            log.debug("视频上传失败，任务id{}，objectName{}",mediaProcess.getId(), objectName);
                            mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(),"3",fileId,null,"上传MP4视频到minio失败");
                            return;
                        }
                        //访问url
                        String url = "/" + bucket + "/" + objectName;
                        //保存任务处理结果
                        mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(),"2",fileId,url,null);
                        tempFile.deleteOnExit();
                    } finally {
                        //计数器减一
                        countDownLatch.countDown();
                    }

                }
            });
        }
        //等待,给一个充裕的超时时间,防止无限等待，到达超时时间还没有处理完成则结束任务
        countDownLatch.await(30,TimeUnit.MINUTES);
    }

    /**
     * 定时处理因线程处理任务突然中断导致任务一直在处理中的状态
     * @throws Exception
     */
    @XxlJob("videoFalseJobHandler")
    public void videoFalseJobHandler() throws Exception {
        //当处理时间超过一个小时 则判定为失败 修改处理中状态为失败
        Boolean b = mediaFileProcessService.updateStatusExceedMaxTime(60);
        if(b){
            log.debug("处理超时任务成功");
        }else{
            log.debug("处理超时任务失败");
            throw new CustomException("处理超时任务失败");
        }
    }

}
